<?php

namespace app\common\logic;



use app\common\utils\Utils;
use think\Db;
use think\Exception;
use think\exception\ValidateException;

/**
 * 会员接口
 */
class ExamLogic
{
    //用户考试计划安排id
    public $userPlanId = null;
    public $row = null;  //试卷信息
    public $userExamRow= null;  //用户考试信息
    public $userPlanRow = null;  //计划安排信息
    public $questionsIds = [];  //本次考试的ids
    public $questions = [];  //本次考试的问题集合
    public $realAnswers = [];  //本次考试的答案集合
    public $answers = [];  //学生答案
    public $time = 0;   //开始时间 单位秒
    public $curIndex = 0;   //当前用户已经答题到的id
    //试卷模型
    protected $model = null;
    protected $userExamsObj = null;
    protected $questionObj = null;
    public function __construct()
    {
        $this->model = new \addons\kaoshi\model\examination\KaoshiExams;
        $this->userExamsObj = new  \addons\kaoshi\model\examination\KaoshiUserExams;
        $this->questionObj = Db::name('KaoshiQuestions');

    }

    /**生成题目*/
    public function getQuestions() {
        if ($this->userPlanId == null) {
            throw new ValidateException('未选择答题试卷！');
        }
        Db::startTrans();
        try {
            $this->getPlan(); //计划安排
            $this->getUserExamRow(); //考试信息

            $this->getPlanQuestion();

            //获取答题时间
            $hours = $this->userPlanRow['hours'] * 60; //分钟
            $starttime = $this->userExamRow['starttime'];
            $has_start = time() - $starttime;
            $this->time = $hours - $has_start;  //值得是还剩下多长时间
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            throw new ValidateException($e->getMessage());
        }
        return $this;
    }

    public function setUserPlanId($user_plan_id) {
        $this->userPlanId = $user_plan_id;
        return $this;
    }

    /**获取考试结果*/

    public function getRes() {
        if ($this->userPlanId == null) {
            throw new ValidateException('未选择答题试卷！');
        }

        $field = ['id', 'user_plan_id', 'score', 'status', 'usetime', 'status', 'starttime', 'lasttime', 'submit_time', 'right_count', 'error_count'];
        try {
            $this->getPlan(); //计划安排
            $this->getUserExamRow([], $field); //考试信息
            if (!$this->userExamRow) {
                throw new ValidateException('未获取到考试信息');
            }
            //获取答题时间
            $hours = $this->userPlanRow['hours'] * 60; //分钟
            //如果用户未交卷 那么强制交卷
            if ($this->userExamRow['status'] == 0) {
                $this->score($this->userExamRow['id']);
                $this->getUserExamRow([], $field);
            }
            $order = $this->getRank($this->userPlanRow['plan_id'], $this->userExamRow['score'], $this->userExamRow['usetime']);
            return [
                'exam' => $this->userExamRow,
                'time' => $hours,
                'order' => $order
            ];
        } catch (\Exception $e) {
            Db::rollback();
            throw new ValidateException($e->getMessage());
        }

    }

    /**
     * 获取计划安排
     */
    public function getPlan() {
        $plan_map = [
            'b.deletetime' => null,
            'b.starttime'  => ['<', time()],
            'b.endtime'    => ['>', time()],
            'a.id'         => $this->userPlanId,
        ];

        //获取考试安排信息
        $user_plan_row = Db::name('KaoshiUserPlan')->alias('a')
            ->join('__KAOSHI_PLAN__ b', 'a.plan_id = b.id')
            ->where($plan_map)
            ->field('a.id,a.user_id,a.plan_id,b.exam_id,b.type,b.endtime,b.starttime,b.type,b.hours,b.times')
            ->find();

        if ($user_plan_row['user_id'] != UID) {
            throw new ValidateException('选择试卷错误');
        }

        $row = $this->model->get($user_plan_row['exam_id']); //获取试卷信息

        $this->row = $row;
        $this->userPlanRow = $user_plan_row;
        return $user_plan_row;
    }

    /**
     * 获取用户考试信息
     * @param $user_plan_row
     * @return array
     * @throws Exception
     */
    public function getUserExamRow($where=[], $field = []) {
        $user_exam_map = ['user_plan_id' => $this->userPlanId];
        if (!$field) {
            $field = true;
        }
        $user_exam_map = array_merge($user_exam_map, $where);
        $user_exam_row = $this->userExamsObj->where($user_exam_map)->field($field)->find(); //正在进行中的考试 同一时间 只有一个
        $this->userExamRow = $user_exam_row;
        return $user_exam_row;
    }

    /**
     * 获取考试题目
     */
    public function getPlanQuestion() {
        if (!$this->userExamRow) {
            $settingdata = json_decode($this->row['settingdata'], true);
            //试卷设置的随机题目信息
            //遍历设置题型，获取题目
            if (!$settingdata) {
                throw new ValidateException('考卷丢失');
            }

            $res = $this->getSettingQuestion($settingdata);
            $this->questionsIds = array_unique($this->questionsIds);
            $real_answers = $this->questionObj->where(['id' => ['in', $this->questionsIds]])->column('id, answer, describe');
            $add = [
                'user_plan_id'     => $this->userPlanId,
                'questionsdata'    => json_encode($this->questions),
                'starttime'        => time(),
                'lasttime'         => time(),
                'real_answersdata' => json_encode($real_answers),
            ];

            $this->userExamsObj->isUpdate(false)->save($add);
            $this->userExamRow = $this->userExamsObj->toArray();
            $this->realAnswers = $real_answers;
        } else {
            $questions = json_decode($this->userExamRow['questionsdata'], true);
            $answers = json_decode($this->userExamRow['answersdata'], true);

            if (is_array($answers) && count($answers) > 0) {
                foreach ($questions as $key => $value) {

                    if ($value['type'] == 2) {
                        foreach ($value['timu'] as $k => $vo) {//1 = 1_2
                            if (isset($answers[(intval($key) + 1)][($k + 1) . '_' . $vo['id']])) {
                                $vo_answer = $answers[(intval($key) + 1)][($k + 1) . '_' . $vo['id']];
                                $answers[(intval($key) + 1)][($k + 1) . '_' . $vo['id']] = explode(',', $vo_answer);
                            }

                        }
                    }
                }
            }
            $this->questions = $questions;
            $this->answers = $answers;
            $this->realAnswers = json_decode($this->userExamRow['real_answersdata'], true);
        }

        return $this;
    }

    public function getSettingQuestion($settingdata) {
        $questions = $settingdata;
        foreach ($settingdata as $key => $value) {
            $map['type'] = $value['type'];
            $map['level'] = $value['level'];
            $map['status'] = 1;
            $map['subject_id'] = $this->row['subject_id'];
            $map['deletetime'] = null;
            //所需题目字段
            $question_field = 'id,question,selectdata,type';

            //获取类型和等级相符的题目
            $arr = $this->questionObj->field($question_field)->where($map)->select();
            //所需题数
            $questions_num = intval($value['number']);
            //随机出的键值数组
            $rand_arr = [];
            //正确答案数组
            //避免考卷生成后，删除了题目，造成所需题数大于题库数量
            //将重复选取题目
            while ($questions_num > 0) {
                if (count($arr) < $value['number']) {
                    //当题库数量不足时，选取的题目会重复
                    //array_rand 第二个参数大于1时返回数组
                    $samll_arr = array_rand($arr, count($arr));
                    if (is_array($samll_arr)) {
                        $rand_arr = array_merge($rand_arr, $samll_arr);
                    } else {
                        array_push($rand_arr, $samll_arr);
                    }
                    $questions_num = $questions_num - count($arr);
                } else {
                    $rand_arr = array_rand($arr, $questions_num);
                    break;
                }
            }

            if (is_array($rand_arr) && count($rand_arr) > 0) {
                foreach ($rand_arr as $k => $v) {
                    array_push($this->questionsIds, $arr[$v]['id']);
                    $arr[$v]['selectdata'] = is_array($arr[$v]['selectdata']) ? $arr[$v]['selectdata'] : json_decode($arr[$v]['selectdata'], true);
                    $questions[$key]['timu'][intval($k)] = $arr[$v];

                }

                shuffle($questions[$key]['timu']);
            } else {
                array_push($this->questionsIds, $arr[$rand_arr]['id']);
                $questions[$key]['timu'][0] = $arr[$rand_arr];
                $questions[$key]['timu'][0]['selectdata'] = json_decode($arr[$rand_arr]['selectdata'], true);

            }

        }

        $this->questions = $questions;

        return ['questions' =>$questions, 'questionsIds' => $this->questionsIds];
    }

    /**
     * 生成api接口数据
     */
    public function apiData() {
        $questions = [];
        //把question进行重组
        foreach ($this->questions as $k => $v) {
            $timu = $v['timu'];
            $timu_info = [
                'type' => $v['type'],       //题目类型
                'level' => $v['level'],     //题目难易成都
                'mark' => $v['mark'],       //题目分值
                'type_index' => $k + 1         //试卷随机分类的索引 + 1
            ];
            foreach ($timu as $k1 => $v1) {
                $timu_info['timu_index'] = $k1 + 1;  //题目所在类别的顺序 索引+1
                $timu_info['timu_id'] = $v1['id'];  //题目id
                $v1['timu_info'] = $timu_info;  //题目信息
                $is_answer = $this->isAnswer($k+1, $k1+1, $v1, $this->answers, $this->realAnswers);
                if ($is_answer == false) {
                    $v1['is_answer'] = 0; //代表未答题
                } else {
                    $v1['is_answer'] = $is_answer;
                    //当前答题到的索引
                    $this->curIndex = count($questions);
                }

                array_push($questions, $v1);
            }
        }

        return $questions;
    }

    /**
     * 是否已经答题 如果答题了 那么就返回结果
     * @param int $k 指的是 题目分类的索引 + 1
     * @param int $k1 指的是 题目的索引 + 1
     * @param mixed $item 题目信息
     *
     * @return mixed
     */
    public function isAnswer($k, $k1, $item, $answer, $realAnswers) {
        if (isset($answer[$k])) {
            $timu_key = $k1 . '_' . $item['id'];
            //正确答案
            $realAnswer = $realAnswers[$item['id']]; //id answer describe
            if (isset($answer[$k][$timu_key])) {//代表已经答题
                $is_right = 0; //错误
                //当前答案
                $curAnswer = $answer[$k][$timu_key];
                //是否答题正确
                $strAnswer = is_array($curAnswer) ? Utils::implodeArr($curAnswer) : $curAnswer;
                if ($strAnswer == $realAnswer['answer']) {
                    $is_right = 1; //正确
                }

                return [
                    'cur_answer' => $curAnswer,  //当前答案
                    'real_answer' => $realAnswer,  //当前真实答案
                    'is_right' => $is_right  //是否正确
                ];

            } else {
                return false;
            }
        } else {
            return false;
        }
    }


    /**
     * 提交答案
     * $id 考试id
     * $user_id用户id
     * $timu_info 指的是题目信息
     * $user_answer 代表的是 用户答案 array
     */
    public function save($id, $user_id, $timu_info, $user_answer) {
        $userExam = $this->userExamsObj->where(['id'=> $id])->find();
        if (!$userExam) {
            throw new ValidateException('考试不存在');
        }
        #考试计划
        $plan = Db::name('KaoshiUserPlan')->where([
            'id' => $userExam['user_plan_id']
        ])->find();
        
        if(!$plan){
            throw new ValidateException('考试计划不存在');
        }

        if ($plan['user_id'] != $user_id) {
            throw new ValidateException('非自己的考卷不能答题');
        }

        $answers = json_decode($userExam['answersdata'], true);
        $realAnswers = json_decode($userExam['real_answersdata'], true);

        $type_index = $timu_info['type_index'];
        $timu_key = $timu_info['timu_index'] . '_' . $timu_info['timu_id'];

        $answerData = Utils::implodeArr($user_answer);
        $item = ['id'=> $timu_info['timu_id']];

        if (isset($answers[$type_index])) {
            if (isset($answers[$type_index][$timu_key])) {
                if($answers[$type_index][$timu_key] != $answerData)
                throw new ValidateException('已经答题成功，不能修改答案');
                else
                return $this->isAnswer($type_index, $timu_info['timu_index'], $item, $answers, $realAnswers);
            } else {
                $answers[$type_index][$timu_key] = $answerData;
            }
        } else {
            $answers[$type_index][$timu_key] = $answerData;
        }

        $userExam->answersdata = json_encode($answers);
        $real_answer = $this->isAnswer($type_index, $timu_info['timu_index'], $item, $answers, $realAnswers);
        if ($real_answer['is_right'] == 1) {
            $userExam->right_count = $userExam['right_count'] + 1;
        } else {
            $userExam->error_count = $userExam['error_count'] + 1;
        }
        $userExam->save();
        //第一次修改统计是否正确
        return $real_answer;
    }

    /**
     * 交卷
     * $id 用户考卷id
     */
    public function score($id = null)
    {
        if ($id === null) {
            throw new ValidateException('请传递试卷id');
        }

        $user_exams_row = $this->userExamsObj
            ->alias('a')
            ->join('__KAOSHI_USER_PLAN__ b', 'b.id = a.user_plan_id', 'left')
            ->join('__KAOSHI_PLAN__ c', 'c.id = b.plan_id', 'left')
            ->field('a.id, a.status,b.plan_id,a.score, a.starttime,a.answersdata,a.real_answersdata,a.questionsdata, c.type, c.hours,c.endtime as kaoshi_endtime')
            ->where('a.id', $id)
            ->find();
        if ($user_exams_row['status'] == 1) {
            throw new ValidateException('已经交卷');
        }
        $real_answers = json_decode($user_exams_row['real_answersdata'], true);
        $questions = json_decode($user_exams_row['questionsdata'], true);
        $score = $user_exams_row['score'];

        $answers = json_decode($user_exams_row['answersdata'], true);
        if (is_array($answers) && count($answers) > 0) {
            foreach ($answers as $key => $value) {
                foreach ($value as $k => $vo) {
                    $timu_id = explode('_', $k)[1];

                    if (is_array($vo)) {
                        $answers[$key][$k] = implode(',', $vo);
                    }
                    if ($real_answers[$timu_id]['answer'] == $answers[$key][$k]) {
                        $scorelist[$key][$k] = $questions[intval($key) - 1]['mark'];
                        $score += $questions[intval($key) - 1]['mark'];
                    } else {
                        $scorelist[$key][$k] = 0;
                    }

                }

            }
        }
        $kaoshitime = $user_exams_row['hours'] * 60;

        $usetime = self::getTime($kaoshitime,  $user_exams_row['starttime'], $user_exams_row['kaoshi_endtime']);
        $update = [
            //'answersdata'   => json_encode($answers),
            'scorelistdata' => json_encode($scorelist),
            'score'         => $score,
            'status'        => 1,
            'submit_time'   => time(),
            'usetime'       => $usetime   //统计用时间
        ];
        $this->userExamsObj->where('id = ' . $id)->update($update);

        return $update;
    }


    public static function getTime($hours, $starttime,$endtime) {
        //用时
        if ($endtime <= time()) {
            //考试已经结束
            $remain = $endtime - $starttime;
            if ($remain >= $hours) {
                $usetime = $hours;
            } else {
                $usetime = $remain;
            }
        } else {
            //当前时间未结束
            $cur = time() - $starttime; //用时多长时间
            if ($cur >= $hours) {
                $usetime = $hours;
            } else {
                $usetime = $cur;
            }
        }

        return $usetime;
    }

    //获取考试排名
    public function getRank($plan_id, $score, $usetime) {
        $count = $this->userExamsObj
            ->where('user_plan_id', 'IN', function($query) use ($plan_id) {
                $query->name('kaoshi_user_plan')->where(['plan_id' => $plan_id])->field('id');
            })
            ->where(['status' => 1, 'score' => ['gt', $score]])
            ->whereOr(function ($query) use ($usetime, $score){
                $query->where(['score'=> $score, 'usetime' => ['lt', $usetime]]);
            })->group('score, usetime')->count();


        return $count + 1;
    }

}
